package algo3.algothief.modelo;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;

import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import algo3.algothief.excepcion.LadronNoRoboAunException;


public class Caso {
	
	private Ladron ladronBuscado;
	
	private Pais siguientePaisEnElCaso;
	
	private ArrayList<Pais> paisesRecorridosPorElPolicia;
	
	private HashMap<String,String> pistasPorCaracteristica;
	private HashMap<Integer,String> enterosPorCaracteristica;

	private boolean policiaEstaEnUltimoPais;
	
//-------------------------------------------------------------------------------------

	public Caso(Ladron ladron) throws ParserConfigurationException, SAXException, IOException, LadronNoRoboAunException {
		if (ladron.verCantidadDePaisesVisitados() == null)
			throw new LadronNoRoboAunException();
		
		this.ladronBuscado = ladron; // IMPORTANTE: el ladron ya robo afuera del caso
	
		this.paisesRecorridosPorElPolicia = new ArrayList<Pais>();
		
		this.pistasPorCaracteristica = this.crearPistasPorCaracteristica();
		this.enterosPorCaracteristica = this.crearEnterosPorCaracteristica();
		
		this.avanzarAlSiguientePais();
		
		this.policiaEstaEnUltimoPais = false;
	}
	
	private Caso(Ladron ladron, Pais siguientePais, ArrayList<Pais> paisesRecorridos, boolean policiaEnUltimoPais) {
		this.ladronBuscado = ladron;
		this.siguientePaisEnElCaso = siguientePais;
		this.paisesRecorridosPorElPolicia = paisesRecorridos;
		this.policiaEstaEnUltimoPais = policiaEnUltimoPais;
		
		this.pistasPorCaracteristica = this.crearPistasPorCaracteristica();
		this.enterosPorCaracteristica = this.crearEnterosPorCaracteristica();
	}

//-------------------------------------------------------------------------------------

	public Element serializar(Document doc) {
		Integer cantidadDePaises = paisesRecorridosPorElPolicia.size();
		Element elementoCaso = doc.createElement("Caso");
		elementoCaso.setAttribute("cantidadDePaisesRecorridos", cantidadDePaises.toString());
		
		Element elementoLadron = ladronBuscado.serializar(doc);
		
		Element elementoPoliciaEnUltimoPais = doc.createElement("ElPoliciaEstaEnElUltimoPais");
		elementoPoliciaEnUltimoPais.setAttribute("condicion", Boolean.toString(policiaEstaEnUltimoPais));
		
		elementoCaso.appendChild(elementoLadron);
		elementoCaso.appendChild(elementoPoliciaEnUltimoPais);
					
		return elementoCaso;
	}
		
	public static Caso hidratar(Document doc, ArrayList<Pais> paisesDelMundo) throws ParserConfigurationException, SAXException, IOException {
		Element elementoCaso = (Element)doc.getElementsByTagName("Caso").item(0);
		String cantidadDePaisesRecorridosEnFormaDeString = elementoCaso.getAttribute("cantidadDePaisesRecorridos");
		int cantidadDePaisesRecorridos = Integer.parseInt(cantidadDePaisesRecorridosEnFormaDeString);
		
		Ladron ladron = Ladron.hidratar(doc, paisesDelMundo);
		
		Pais siguientePais = Caso.cargarSiguientePais(ladron);
		ArrayList<Pais> paisesRecorridos = Caso.cargarPaisesRecorridos(ladron, cantidadDePaisesRecorridos);
		
		Element elementoPoliciaEnUltimoPais = (Element)elementoCaso.getElementsByTagName("ElPoliciaEstaEnElUltimoPais").item(0);
		String policiaEnUltimoPaisEnFormaDeString = elementoPoliciaEnUltimoPais.getAttribute("condicion");
		boolean policiaEnUltimoPais = Boolean.parseBoolean(policiaEnUltimoPaisEnFormaDeString);
		
		Caso casoNuevo = new Caso(ladron,siguientePais,paisesRecorridos,policiaEnUltimoPais);
		
		return casoNuevo;
	}
	
//-------------------------------------------------------------------------------------
	
	private String definirElOEllaSegunElSexoDelLadron(DescripcionDeLadron descripcion) {
		String sexo = descripcion.verCaracteristica("Sexo");
		
		if (sexo.matches("Masculino"))
			return "El";
		return "Ella";
	}
	
	private HashMap<String, String> crearPistasPorCaracteristica() {
		HashMap<String, String> pistasPorCaracteristica = new HashMap<String, String>();

		DescripcionDeLadron descripcion = ladronBuscado.verDescripcion();
		String elOElla = this.definirElOEllaSegunElSexoDelLadron(descripcion);
		
		pistasPorCaracteristica.put("Hobby", elOElla + " comento que practicaba " + descripcion.verCaracteristica("Hobby") + ".");
		pistasPorCaracteristica.put("Cabello", "Tenia el cabello " + descripcion.verCaracteristica("Cabello") + ".");
		pistasPorCaracteristica.put("Senia", elOElla + " llevaba un " + descripcion.verCaracteristica("Senia") + ".");
		pistasPorCaracteristica.put("Vehiculo", "Conducia un " + descripcion.verCaracteristica("Vehiculo") + ".");
		
		return pistasPorCaracteristica;
	}
	
	private HashMap<Integer, String> crearEnterosPorCaracteristica() {
		HashMap<Integer, String> enterosPorCaracteristica = new HashMap<Integer, String>();
		
		enterosPorCaracteristica.put(0, "Hobby");
		enterosPorCaracteristica.put(1, "Cabello");
		enterosPorCaracteristica.put(2, "Senia");
		enterosPorCaracteristica.put(3, "Vehiculo");
		
		return enterosPorCaracteristica;
	}
	
	private static Pais cargarSiguientePais(Ladron ladron) {
		RutaDeEscape ruta = ladron.verRutaDeEscape();
		int cantidadDePaisesIterados = ruta.verCantidadDePaisesIterados();
		
		Pais pais = ruta.verElPaisNumero(cantidadDePaisesIterados - 1);
		
		return pais;
	}
	
	private static ArrayList<Pais> cargarPaisesRecorridos(Ladron ladron, int cantidadDePaisesRecorridos) {
		ArrayList<Pais> paisesRecorridos = new ArrayList<Pais>();
		
		RutaDeEscape ruta = ladron.verRutaDeEscape();
		for (int i = 0; i < cantidadDePaisesRecorridos; i++) {
			Pais pais = ruta.verElPaisNumero(i);
			paisesRecorridos.add(pais);
		}
		
		return paisesRecorridos;
	}
	
	
	
	private RutaDeEscape verLaRutaDeEscape() {
		return ladronBuscado.verRutaDeEscape();
	}
	
	private ArrayList<Pais> verLosPaisesDeLaRutaDeEscape() {
		ArrayList<Pais> paisesDeLaRuta = new ArrayList<Pais>();
		
		RutaDeEscape ruta = this.verLaRutaDeEscape();
		int cantidadDePaisesDeLaRuta = ruta.verCantidadDePaisesVisitados();
		for (int i = 0; i < cantidadDePaisesDeLaRuta; i++) {
			Pais paisDeLaRuta = ruta.verElPaisNumero(i);
			paisesDeLaRuta.add(paisDeLaRuta);
		}
		
		return paisesDeLaRuta;
	}
	
	
	private void intentarAvanzarEnElCaso(Pais paisActual) throws ParserConfigurationException, SAXException, IOException {
		boolean esteEsElSiguientePaisEnElCaso = this.verSiElPaisEsElSiguienteEnElCaso(paisActual);
		
		if (esteEsElSiguientePaisEnElCaso) 
			this.avanzarEnElCaso();
		else 
			// Esto sirve en el caso en el que el policia llego al ultimo pais, pero viaja a otro.
			this.policiaEstaEnUltimoPais = false;
	}
	
	private void avanzarAlSiguientePais() throws ParserConfigurationException, SAXException, IOException {
		RutaDeEscape rutaDelLadronBuscado = this.verLaRutaDeEscape();
		Pais paisDondeSeProduceElRobo = rutaDelLadronBuscado.siguientePaisEnLaRuta();
		this.paisesRecorridosPorElPolicia.add(paisDondeSeProduceElRobo); // para poder restablecerse al final.
		
		boolean hayPaisesEnLaRutaDeEscape = rutaDelLadronBuscado.quedanPaisesEnLaRuta();
		
		if (hayPaisesEnLaRutaDeEscape) {
			this.siguientePaisEnElCaso = rutaDelLadronBuscado.siguientePaisEnLaRuta();
			paisDondeSeProduceElRobo.modificarPistasParaApuntarAlProximoPais(siguientePaisEnElCaso);
			
			this.agregarPistaSobreElLadronAlPais(paisDondeSeProduceElRobo);
		} 
		else 
			this.policiaEstaEnUltimoPais = true;
	}
	

	
	private int generarEnteroAlAzarEntre(int minimoValor, int maximoValor) {
		Random generadorAleatorio = new Random();
		int numeroAlAzar = generadorAleatorio.nextInt(maximoValor - minimoValor + 1) + minimoValor;
	
		return numeroAlAzar;
	}
	
	private String obtenerCaracteristicaAlAzar() {
		int cantidadDeCaracteristicas = pistasPorCaracteristica.size();
		Integer numeroAlAzar = this.generarEnteroAlAzarEntre(0, cantidadDeCaracteristicas-1);
		
		String caracteristicaAlAzar = enterosPorCaracteristica.remove(numeroAlAzar);
		
		for (int i = numeroAlAzar; i < cantidadDeCaracteristicas; i++) {
			String caracteristicaSiguiente = enterosPorCaracteristica.get(i);
			enterosPorCaracteristica.put(numeroAlAzar, caracteristicaSiguiente);
		}
		
		return caracteristicaAlAzar;
	}
	
	private void agregarPistaSobreElLadronAlPais(Pais pais) {
		if (pistasPorCaracteristica.size() > 0) {
			String caracteristicaAlAzar = this.obtenerCaracteristicaAlAzar();
			String pista = pistasPorCaracteristica.remove(caracteristicaAlAzar);
			
			pais.agregarPistaSobreLadron(pista);
		}
	}
	
//-------------------------------------------------------------------------------------

	public String verNombreDelLadron() {
		DescripcionDeLadron descripcionDelLadron = ladronBuscado.verDescripcion();
		return (descripcionDelLadron.verNombre());
	}
	
	public DescripcionDeLadron verDescripcionDelLadron() {
		return ladronBuscado.verDescripcion();
	}
	
	public Ladron verLadron() {
		return this.ladronBuscado;
	}

	
	public Pais verElPaisInicialDelCaso() {
		RutaDeEscape ruta = this.verLaRutaDeEscape();
		return ruta.verElPaisNumero(0);
	}
	
	public Pais verElSiguientePaisEnElCaso() {
		return this.siguientePaisEnElCaso;
	}
	
	public boolean verSiElPaisEsElSiguienteEnElCaso(Pais pais) {
		return (siguientePaisEnElCaso == pais);
	}
	
	public ArrayList<Pais> verRutaTomadaPorElLadron(){
		return this.verLosPaisesDeLaRutaDeEscape();
	}
	
	
	public void avanzarEnElCaso() throws ParserConfigurationException, SAXException, IOException {
		Pais paisAnteriorEnElCaso = this.siguientePaisEnElCaso;
		this.paisesRecorridosPorElPolicia.add(paisAnteriorEnElCaso); // Para poder restablecerse al final.
		
		RutaDeEscape rutaDelLadronBuscado = this.verLaRutaDeEscape();
		boolean hayPaisesEnLaRutaDeEscape = rutaDelLadronBuscado.quedanPaisesEnLaRuta();
		
		if (hayPaisesEnLaRutaDeEscape) {
			this.siguientePaisEnElCaso = rutaDelLadronBuscado.siguientePaisEnLaRuta();
			paisAnteriorEnElCaso.modificarPistasParaApuntarAlProximoPais(siguientePaisEnElCaso);
			
			this.agregarPistaSobreElLadronAlPais(paisAnteriorEnElCaso);
		} 
		else 
			this.policiaEstaEnUltimoPais = true;
		
		if (policiaEstaEnUltimoPais) 
			siguientePaisEnElCaso.esconderLadronEnAlgunLugar();
	}

	public int verCantidadDePaisesRestantes() {
		int cantidadDePaisesRecorridosPorElLadron = ladronBuscado.verCantidadDePaisesVisitados();
		int cantidadDePaisesRecorridosPorElPolicia = paisesRecorridosPorElPolicia.size();
		
		return cantidadDePaisesRecorridosPorElLadron - cantidadDePaisesRecorridosPorElPolicia;
	}
	
	
	public int viajarAUnPais(Policia policia, Pais paisDestino) throws ParserConfigurationException, SAXException, IOException {
		Pais paisActual = policia.verPaisDondeEsta();
		Viaje viaje = new Viaje(paisActual, paisDestino);
		
		int horasTranscurridas = viaje.tiempoDeViaje(policia);
	
		policia.modificarPaisActual(paisDestino);
		this.intentarAvanzarEnElCaso(paisDestino);
		
		return horasTranscurridas;
	}
	
	public String visitarUnLugar(Policia policia, LugarDePais lugar) {
		RangoPolicia rangoActual = policia.verRango();
		String pistaParaElPolicia = rangoActual.pedirPista(lugar);
		
		return pistaParaElPolicia;
	}
	
	public boolean policiaEstaEnUltimoPais() {
		return this.policiaEstaEnUltimoPais;
	}
	
	public void finalizarCaso() throws ParserConfigurationException, SAXException, IOException {
		for (Pais paisDeLaRuta : paisesRecorridosPorElPolicia)
			paisDeLaRuta.restablecerPistas();
	}
}
